home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
libgutil
/
qquant.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
8KB
|
406 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
* qquant -
* quick quant. The median cut algorithm follows. This is
* based on heckbert's median cut algoirthm.
*
* Paul Haeberli - 1991
*/
#include "stdio.h"
#include "quant.h"
#define DEBUG
#define OFFSET_R 3
#define OFFSET_G 2
#define OFFSET_B 1
#define OFFSET_L 0
#define MAXCOLORS (256)
#define NINCHUNK (100)
#define RDIR (1)
#define GDIR (2)
#define BDIR (3)
typedef struct apixel {
struct apixel *next;
unsigned char *pix;
} apixel;
typedef struct colorvol {
struct colorvol *next;
long rmin, rmax, rcut, rdev;
long gmin, gmax, gcut, gdev;
long bmin, bmax, bcut, bdev;
int avgdev;
int npixels;
int splitdir;
int number;
apixel *pix;
} colorvol;
static long ctab[MAXCOLORS][3];
static short rbuf[MAXCOLORS];
static short gbuf[MAXCOLORS];
static short bbuf[MAXCOLORS];
static colorvol *cvarray[MAXCOLORS];
static colorvol *fvols = 0;
static apixel *fpixs = 0;
static colorvol *root = 0;
static int nnodes;
static freevol( cv )
colorvol *cv;
{
if( cv ) {
cv->next = fvols;
fvols = cv;
}
}
static colorvol *newvol()
{
colorvol *cv;
int i;
if(!fvols) {
cv = (colorvol *)mymalloc(NINCHUNK*sizeof(colorvol));
for(i=0; i<NINCHUNK; i++)
freevol(cv++);
}
cv = fvols;
fvols = fvols->next;
return cv;
}
static freepix( p )
apixel *p;
{
if( p ) {
p->next = fpixs;
fpixs = p;
}
}
static apixel *newpix()
{
apixel *p;
int i;
if(!fpixs) {
p = (apixel *)mymalloc(NINCHUNK*sizeof(apixel));
for(i=0; i<NINCHUNK; i++)
freepix(p++);
}
p = fpixs;
fpixs = fpixs->next;
return p;
}
static zaphist(hist,min,max)
long hist[];
long min, max;
{
int i;
for(i=min; i<=max; i++)
hist[i] = 0;
}
static rangehist(hist,min,max,npix,ravg,rdev)
long hist[];
long min, max, npix, *ravg, *rdev;
{
int i;
long delta, avg, dev;
avg = 0;
for(i=min; i<=max; i++) {
avg += i*hist[i];
}
avg = avg/npix;
dev = 0;
for(i=min; i<=max; i++) {
delta = i-avg;
dev += hist[i]*delta*delta;
}
*ravg = avg;
*rdev = dev>>8;
if(avg == min)
return 0;
else
return 1;
}
#define FLAGMAX (12000)
static makenode(rmin,rmax,gmin,gmax,bmin,bmax,pix)
int rmin, rmax, gmin, gmax, bmin, bmax;
apixel *pix;
{
colorvol *cv;
int Rmin, Rmax, Gmin, Gmax, Bmin, Bmax;
int r, g, b;
int rok, gok, bok;
long npixels;
long rhist[256];
long ghist[256];
long bhist[256];
cv = newvol();
cv->pix = pix;
Rmin = Gmin = Bmin = FLAGMAX;
Rmax = Gmax = Bmax = -FLAGMAX;
zaphist(rhist,rmin,rmax);
zaphist(ghist,gmin,gmax);
zaphist(bhist,bmin,bmax);
npixels = 0;
while(pix) {
r = pix->pix[OFFSET_R];
g = pix->pix[OFFSET_G];
b = pix->pix[OFFSET_B];
if(r<Rmin)
Rmin = r;
if(g<Gmin)
Gmin = g;
if(b<Bmin)
Bmin = b;
if(r>Rmax)
Rmax = r;
if(g>Gmax)
Gmax = g;
if(b>Bmax)
Bmax = b;
npixels++;
rhist[r]++;
ghist[g]++;
bhist[b]++;
pix = pix->next;
}
if(npixels == 0)
return;
cv->npixels = npixels;
cv->rmin = Rmin;
cv->gmin = Gmin;
cv->bmin = Bmin;
cv->rmax = Rmax;
cv->gmax = Gmax;
cv->bmax = Bmax;
rok = rangehist(rhist,cv->rmin,cv->rmax,npixels,&cv->rcut,&cv->rdev);
gok = rangehist(ghist,cv->gmin,cv->gmax,npixels,&cv->gcut,&cv->gdev);
bok = rangehist(bhist,cv->bmin,cv->bmax,npixels,&cv->bcut,&cv->bdev);
if(rok && cv->rdev>=cv->gdev && cv->rdev>=cv->bdev)
cv->splitdir = RDIR;
else if(gok && cv->gdev>=cv->rdev && cv->gdev>=cv->bdev)
cv->splitdir = GDIR;
else if(bok)
cv->splitdir = BDIR;
else
cv->splitdir = 0;
cv->avgdev = cv->rdev+cv->gdev+cv->bdev;
#ifdef OLDWAY
cv->avgdev = ILUM(cv->rdev,cv->gdev,cv->bdev);
#endif
cv->next = root;
root = cv;
nnodes++;
}
splitlist(pix,pix0,pix1,level,dir)
apixel *pix;
apixel **pix0, **pix1;
int level, dir;
{
apixel *p0, *p1, *np;
p0 = 0;
p1 = 0;
switch(dir) {
case RDIR:
while(pix) {
np = pix->next;
if((int)(pix->pix[OFFSET_R])<level) {
pix->next = p0;
p0 = pix;
} else {
pix->next = p1;
p1 = pix;
}
pix = np;
}
break;
case GDIR:
while(pix) {
np = pix->next;
if((int)(pix->pix[OFFSET_G])<level) {
pix->next = p0;
p0 = pix;
} else {
pix->next = p1;
p1 = pix;
}
pix = np;
}
break;
case BDIR:
while(pix) {
np = pix->next;
if((int)(pix->pix[OFFSET_B])<level) {
pix->next = p0;
p0 = pix;
} else {
pix->next = p1;
p1 = pix;
}
pix = np;
}
break;
}
*pix0 = p0;
*pix1 = p1;
}
static divspace(cv)
colorvol *cv;
{
apixel *pix0, *pix1;
switch(cv->splitdir) {
case RDIR:
splitlist(cv->pix,&pix0,&pix1,cv->rcut,RDIR);
makenode(cv->rmin,cv->rcut-1,cv->gmin,cv->gmax,cv->bmin,cv->bmax,pix0);
makenode(cv->rcut,cv->rmax, cv->gmin,cv->gmax,cv->bmin,cv->bmax,pix1);
break;
case GDIR:
splitlist(cv->pix,&pix0,&pix1,cv->gcut,GDIR);
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gcut-1,cv->bmin,cv->bmax,pix0);
makenode(cv->rmin,cv->rmax,cv->gcut,cv->gmax, cv->bmin,cv->bmax,pix1);
break;
case BDIR:
splitlist(cv->pix,&pix0,&pix1,cv->bcut,BDIR);
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bmin,cv->bcut-1,pix0);
makenode(cv->rmin,cv->rmax,cv->gmin,cv->gmax,cv->bcut,cv->bmax ,pix1);
break;
default:
fprintf(stderr,"baaaaaad poop %d\n",cv->splitdir);
exit(1);
}
nnodes--;
cv->pix = 0;
cv->npixels = 0;
cv->avgdev = 0;
}
cmap *qquant( unsigned long *lbuf, unsigned short *sbuf, int npixels, int baseci, int ncolors, int mode )
{
int i, r, g, b;
int ci, bigest;
colorvol *bigcv, *cv, *ncv;
apixel *pix, *pixlist, *npix;
int n;
/* check ncolors */
if(ncolors<1) {
fprintf(stderr,"quant: num colors is less than 1\n");
exit(1);
}
if(ncolors>MAXCOLORS) {
fprintf(stderr,"quant: num colors exceeds %d\n",MAXCOLORS);
exit(1);
}
/* make a list out of all the pixels */
pixlist = 0;
n = npixels;
for(n=0; n<npixels; n++) {
pix = newpix();
pix->next = pixlist;
pixlist = pix;
pix->pix = (unsigned char *)(lbuf+n);
}
/* create the root node */
root = NULL;
makenode(0,255,0,255,0,255,pixlist);
/* repeat division of the best piece for ncolors */
nnodes = 1;
while(nnodes<ncolors) {
bigcv = 0;
bigest = 0;
cv = root;
if(mode) { /* use number of pixels */
while(cv) {
if(cv->splitdir && cv->npixels>bigest) {
bigcv = cv;
bigest = cv->npixels;
}
cv = cv->next;
}
} else { /* use avg deviation */
while(cv) {
if(cv->splitdir && cv->avgdev>bigest) {
bigcv = cv;
bigest = cv->avgdev;
}
cv = cv->next;
}
}
if(bigcv)
divspace(bigcv);
else {
#ifdef NOTDEf
fprintf(stderr,"Ncolor reduced to %d from %d\n",nnodes,ncolors);
#endif
ncolors = nnodes;
break;
}
}
/* set the color indexes */
cv = root;
ci = 0;
while(cv) {
pix = cv->pix;
if(pix) {
while(pix) {
sbuf[(pix->pix-((unsigned char *)lbuf))/sizeof(long)] = baseci+ci;
npix = pix->next;
freepix(pix);
pix = npix;
}
rbuf[ci] = cv->rcut;
gbuf[ci] = cv->gcut;
bbuf[ci] = cv->bcut;
ci++;
}
ncv = cv->next;
freevol(cv);
cv = ncv;
}
return newcmap(rbuf,gbuf,bbuf,0,ci);
}